package com.suppor.rpc.client.proxy;

import com.suppor.rpc.client.annotation.RpcReference;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;

@Component
public class ClientPostProcess implements BeanClassLoaderAware, BeanFactoryPostProcessor {

    private ClassLoader classLoader;

    // 记录RpcReference 注解的类的字段
    private ConcurrentMap<String, BeanDefinition> beanDefinitionConcurrentMap = new ConcurrentHashMap<>();


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        // bean 加载之后，初始化之前执行，遍历所有的bean, 把使用了RpcReference注解的字段都记录下，然后做远程调用的修改等
        for (String beanDefinitionName : factory.getBeanDefinitionNames()) {
            BeanDefinition beanDefinition = factory.getBeanDefinition(beanDefinitionName);
            String className = beanDefinition.getBeanClassName();
            if (null != className) {
                Class<?> clazz = ClassUtils.resolveClassName(className, this.classLoader);
                ReflectionUtils.doWithFields(clazz, field -> {
                    parseRpcReference(field);
                });
            }
        }
        // 完成bean的注册
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
        this.beanDefinitionConcurrentMap.forEach(new BiConsumer<String, BeanDefinition>() {
            @Override
            public void accept(String className, BeanDefinition beanDefinition) {
                if (registry.containsBeanDefinition(className)) {
                    throw new IllegalArgumentException("Spring Context Already Contain Bean: " + className);
                }
                registry.registerBeanDefinition(className, beanDefinition);// bean注册
            }
        });
    }

    private void parseRpcReference(Field field) {
        RpcReference rpcReference = field.getAnnotation(RpcReference.class);
        if (null != rpcReference) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ClientBean.class);
            builder.setInitMethodName("init");
            builder.addPropertyValue("serviceClass", field.getType());
            builder.addPropertyValue("registerAdd", rpcReference.registryAddr());
            builder.addPropertyValue("registerType", rpcReference.registerType());
            builder.addPropertyValue("timeOut", rpcReference.timeout());
            builder.addPropertyValue("serviceVersion", rpcReference.version());
            BeanDefinition definition = builder.getBeanDefinition();
            beanDefinitionConcurrentMap.put(field.getName(), definition);
        }
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }
}
